package org.amse.korovkin.ciphering.model;
import org.amse.korovkin.ciphering.model.cipher.monocipher.ChangeTable;
import java.io.*;
import org.amse.korovkin.ciphering.Main;

public class TableOfFrequency {
   
    private int myNum = 0;
    private char[] myLetter = new char[Main.NUM_LETTERS];
    private double[] myFrequency = new double[Main.NUM_LETTERS];

    public int getNum() {
        return myNum;
    }
    public char[] getLetters() {
        return myLetter;
    }
    public double[] getFrequencies() {
        return myFrequency;
    }
    public double getFrequency(char ch) {
        ch = ChangeTable.toLowerCase(ch);
        if (this.contains(ch)) {
            for (int i = 0; i < myNum; i++) {
                if (myLetter[i] == ch) {
                    return myFrequency[i];
                }
            } return -1;
        } else return -1;

    }

    public void addChar(char ch, double Frequency) {
        ch = ChangeTable.toLowerCase(ch);
        if (!this.contains(ch)) {
            if (myNum < Main.NUM_LETTERS) {
                myLetter[myNum] = ch;
                myFrequency[myNum] = Frequency;
                myNum++;
            } else {
                throw new ArrayIndexOutOfBoundsException("TableOfFrequency is full. ");
            }
        }         
    }
    public void addChar(char ch) {
        this.addChar(ch, 0);
    }

    /**
     * Remove last added content
     */
    public void remove() {
        myNum--;
        myLetter[myNum] = (char)0;
        myFrequency[myNum] = 0.0;
    }        

    /**
     * Find letter that had Frequency in range [{@code minFrec}; {@code maxFrec}]
     * and with {@code number} in arrangement. If that letter doesn't exist return -1.
     * @param minFrec Minimum frequency that can have search letter
     * @param maxFrec Maximum frequency that can have search letter
     * @param number number of search letter in arragement in range [minFrec; maxFrec]
     * @return integer value of search letter
     */
    public int findLetter(double minFrec, double maxFrec, int number) {
        int counter = 0;
        for (int i = 0; i < myNum; i++) {
            if (myFrequency[i] >= minFrec && myFrequency[i] <= maxFrec) {
                counter++;
                if (counter == number) {
                    return (int)myLetter[i];
                }
            }
        }
        return -1;
    }
    public boolean contains(char ch) {
        ch = ChangeTable.toLowerCase(ch);
        for (int i = 0; i < myNum; i++) {
            if (myLetter[i] == ch) {
                return true;
            } 
        }
        return false;
    }
    /**
     * Increment Frequency of {@code ch} letter by {@code additive}
     * @param ch Letter to frequency increment.
     * @param additive Value of increment.
     */
    public void incFrequency(char ch, int additive) {
        ch = ChangeTable.toLowerCase(ch);
        for (int i = 0; i < myNum; i++) {
            if (myLetter[i] == ch) {
                myFrequency[i] += additive;
                return;
            }
        }
    }
    /**
     * Divide all Frequencies in TableOfFrequency by {@code divizor}.
     * If {@code divizor = 0} throws java.lang.ArithmeticException
     * @param divizor Value of divizor
     * @throws ArithmeticException if {@code divizor = 0}
     */
    public void divFrequency(int divizor) {
        if (divizor != 0) {
            for (int i = 0; i < myNum; i++) {
                myFrequency[i] /= divizor;
            }
        } else {
            throw new ArithmeticException();
        }
    }    

    @Override
    public String toString() {
        StringBuilder res = new StringBuilder();
        for (int i = 0; i < myNum; i++) {
            res.append(myLetter[i]);
            res.append(" " + myFrequency[i] + "\n");
        }
        return res.toString();
    }

    /**
     * Sort TableOfFrequency in arrangement of <code> myLetter[] </code> (from 'a' to 'z')
     */
    public void sortLetters() {
        for (int i = 0; i < myNum; i++) {
            for (int j = i; j < myNum; j++) {
                if (myLetter[i] > myLetter[j]) {
                    char buf = myLetter[i];
                    myLetter[i] = myLetter[j];
                    myLetter[j] = buf;
                    double buffer = myFrequency[i];
                    myFrequency[i] = myFrequency[j];
                    myFrequency[j] = buffer;
                }
            }
        }
    }

    /**
     * Sort TableOfFrequency in arrangement of <code> myFrequency[] </code>
     * <u>from bigger to smaller!</u>
     */
    public void sortFrequences() {
        for (int i = 0; i < myNum; i++) {
            for (int j = i; j < myNum; j++) {
                if (myFrequency[i] < myFrequency[j]) {
                    char buf = myLetter[i];
                    myLetter[i] = myLetter[j];
                    myLetter[j] = buf;
                    double buffer = myFrequency[i];
                    myFrequency[i] = myFrequency[j];
                    myFrequency[j] = buffer;
                }
            }
        }
    }
    public void writeToFile(DataOutputStream stream)
    throws IOException {
        stream.writeInt(myNum);
        for (int i = 0; i < myNum; i++) {
            stream.writeChar(myLetter[i]);
            stream.writeDouble(myFrequency[i]);
        }
    }
    public void readFromFile(String inputFile)
        throws IOException, ArrayIndexOutOfBoundsException{
        DataInputStream dis = new DataInputStream(new FileInputStream(inputFile));
        int n = dis.readInt();
        for (int i = 0; i < n; i++) {
            this.addChar(dis.readChar(), dis.readDouble());
        }
        dis.close();
    }
    public static TableOfFrequency createFictitious() {
        TableOfFrequency table = new TableOfFrequency();
        table.addChar('a', 0.06889);
        table.addChar('b', 0.01018);
        table.addChar('c', 0.02566);
        table.addChar('d', 0.03563);
        table.addChar('e', 0.10796);
        table.addChar('f', 0.02018);
        table.addChar('g', 0.01397);
        table.addChar('h', 0.05041);
        table.addChar('i', 0.05485);
        table.addChar('j', 0.00060);
        table.addChar('k', 0.00288);
        table.addChar('l', 0.02853);
        table.addChar('m', 0.02098);
        table.addChar('n', 0.05847);
        table.addChar('o', 0.05951);
        table.addChar('p', 0.01747);
        table.addChar('q', 0.00085);
        table.addChar('r', 0.05055);
        table.addChar('s', 0.05367);
        table.addChar('t', 0.07607);
        table.addChar('u', 0.02048);
        table.addChar('v', 0.00825);
        table.addChar('w', 0.01649);
        table.addChar('x', 0.00202);
        table.addChar('y', 0.01442);
        table.addChar('z', 0.00028);
        table.addChar(' ', 0.18355);
        table.addChar('.', 0.00795);
        table.addChar(',', 0.01485);
        return table;
    }

}
